/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Includes
#include "SkugoPacketRouter.hpp"
#include "SkugoBuffer.hpp"

namespace Skugo
{
  //***************************************************************************
  PacketRouter::PacketRouter(IConnection* connection, PluginContainer* localPlugins)
  {
    // Store the connection
    this->Connection = connection;

    // Store the local and global plugin containers
    this->LocalPlugins = localPlugins;

    // Initialize the data size
    this->CurrentPacket.DataSize = PacketHeader::InvalidDataSize;
  }

  //***************************************************************************
  void PacketRouter::ProcessAllIncoming()
  {
    // Store the data
    char*    data;
    sizeType size;

    // Attempt to read all data in from the link...
    while (this->Connection->Read(&data, &size))
    {
      // Add the data to the array
      this->Data.insert(this->Data.end(), data, data + size);

      // Delete the old data buffer (it is no longer needed)
      delete[] data;
      data = nullptr;
    }

    // Should we continue reading packets in? (for the below loop)
    bool contiueParsingPackets = false;

    do
    {
      // Assume we have not read any packets in yet
      contiueParsingPackets = false;

      // If we're currently not working on a packet and we
      // at least have enough data for to read a header size...
      if (this->CurrentPacket.DataSize == PacketHeader::InvalidDataSize && this->Data.size() > PacketHeader::Size)
      {
        // Create a buffer to read the header data
        Buffer headerReader(&this->Data.front(), this->Data.size());

        // Read the packet header...
        this->CurrentPacket.DataSize = headerReader.Read<unsigned>();
        this->CurrentPacket.Type = headerReader.Read<unsigned char>();
        this->CurrentPacket.InstanceGuid = headerReader.Read<Guid>();
      }

      // Are we currently working on a packet, and do we have enough data to make a full packet?
      bool fullPacket = this->CurrentPacket.DataSize != PacketHeader::InvalidDataSize &&
                        this->Data.size() >= (this->CurrentPacket.DataSize + PacketHeader::Size);

      // Check if we are now reading a packet and if we have enough data to read the packet
      if (fullPacket)
      {
        // Copy the data into a newly created packet buffer
        char* newData = new char[this->CurrentPacket.DataSize];
        memcpy(newData, (&this->Data.front()) + PacketHeader::Size, this->CurrentPacket.DataSize);

        // Find the corresponding local plugin and route the data to it
        Plugin* local = this->LocalPlugins->GetPlugin(this->CurrentPacket.InstanceGuid);

        // If we found the local plugin
        if (local != nullptr)
        {
          // Route the packet to the local plugin
          local->Receive(newData, this->CurrentPacket.DataSize, (PacketType::Enum)this->CurrentPacket.Type);
        }
        else
        {
          // Throw an error since this is not ok!
          SkugoError("A message came for an invalid plugin");
        }

        // Clear all this packet's data from the buffer
        this->Data.erase(this->Data.begin(), this->Data.begin() + PacketHeader::Size + this->CurrentPacket.DataSize);

        // Reset the current packet size
        this->CurrentPacket.DataSize = PacketHeader::InvalidDataSize;

        // We read a packet, we should attempt to continue!
        contiueParsingPackets = true;

        // Free the packet data
        delete[] newData;
      }
    }
    // Loop until we stopped parsing packets
    while (contiueParsingPackets);
  }
}
